inspector: Bring back debug updates
authorBenjamin Otte <otte@redhat.com>
Wed, 4 Apr 2018 11:22:28 +0000 (13:22 +0200)
committerBenjamin Otte <otte@redhat.com>
Thu, 5 Apr 2018 12:58:42 +0000 (14:58 +0200)
This time, they are implemented as an overlay, so they require a running
inspector and can't be enabled via env variable anymore.

gtk/inspector/highlightoverlay.c
gtk/inspector/inspectoroverlay.c
gtk/inspector/inspectoroverlay.h
gtk/inspector/meson.build
gtk/inspector/updatesoverlay.c [new file with mode: 0644]
gtk/inspector/updatesoverlay.h [new file with mode: 0644]
gtk/inspector/visual.c
gtk/inspector/visual.ui
gtk/inspector/window.c

index 869262e148da675592723737e84417b73567ce3f..c45220bc0efb6dec57a7eeb9ac2ea89f81df28be 100644 (file)
@@ -42,6 +42,7 @@ G_DEFINE_TYPE (GtkHighlightOverlay, gtk_highlight_overlay, GTK_TYPE_INSPECTOR_OV
 static void
 gtk_highlight_overlay_snapshot (GtkInspectorOverlay *overlay,
                                 GtkSnapshot         *snapshot,
+                                GskRenderNode       *node,
                                 GtkWidget           *widget)
 {
   GtkHighlightOverlay *self = GTK_HIGHLIGHT_OVERLAY (overlay);
index a2e1c3d070926677e8fc13f9684d04b004d1ae77..9b9678d0ae0d520f1315ee80cd0f4f896cd4a846 100644 (file)
@@ -34,6 +34,7 @@ G_DEFINE_ABSTRACT_TYPE (GtkInspectorOverlay, gtk_inspector_overlay, G_TYPE_OBJEC
 static void
 gtk_inspector_overlay_default_snapshot (GtkInspectorOverlay *self,
                                         GtkSnapshot         *snapshot,
+                                        GskRenderNode       *node,
                                         GtkWidget           *widget)
 {
 }
@@ -58,9 +59,10 @@ gtk_inspector_overlay_init (GtkInspectorOverlay *self)
 void
 gtk_inspector_overlay_snapshot (GtkInspectorOverlay *self,
                                 GtkSnapshot         *snapshot,
+                                GskRenderNode       *node,
                                 GtkWidget           *widget)
 {
-  GTK_INSPECTOR_OVERLAY_GET_CLASS (self)->snapshot (self, snapshot, widget);
+  GTK_INSPECTOR_OVERLAY_GET_CLASS (self)->snapshot (self, snapshot, node, widget);
 }
 
 void
index 867070b3c5db62a0192445362cc468a7a57b1ff5..a38e77726e1da88f355ee48fa64c08b028f11932 100644 (file)
@@ -34,12 +34,14 @@ struct _GtkInspectorOverlayClass
 
   void                  (* snapshot)                            (GtkInspectorOverlay    *self,
                                                                  GtkSnapshot            *snapshot,
+                                                                 GskRenderNode          *node,
                                                                  GtkWidget              *widget);
   void                  (* queue_draw)                          (GtkInspectorOverlay    *self);
 };
 
 void                    gtk_inspector_overlay_snapshot          (GtkInspectorOverlay    *self,
                                                                  GtkSnapshot            *snapshot,
+                                                                 GskRenderNode          *node,
                                                                  GtkWidget              *widget);
 void                    gtk_inspector_overlay_queue_draw        (GtkInspectorOverlay    *self);
 
index 7d8fedec78dceef76d741188c1b6ecfffa342d4c..585855c4228153ee2bd08f6e8ecb693d855ac467 100644 (file)
@@ -35,6 +35,7 @@ inspector_sources = files(
   'statistics.c',
   'strv-editor.c',
   'treewalk.c',
+  'updatesoverlay.c',
   'visual.c',
   'window.c',
 )
diff --git a/gtk/inspector/updatesoverlay.c b/gtk/inspector/updatesoverlay.c
new file mode 100644 (file)
index 0000000..59cf9fe
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "updatesoverlay.h"
+
+#include "gtkintl.h"
+#include "gtkwidget.h"
+
+#include "gsk/gskrendernodeprivate.h"
+
+/* duration before we start fading in us */
+#define GDK_DRAW_REGION_MIN_DURATION 50 * 1000
+/* duration when fade is finished in us */
+#define GDK_DRAW_REGION_MAX_DURATION 200 * 1000
+
+typedef struct {
+  gint64 timestamp;
+  cairo_region_t *region;
+} GtkUpdate;
+
+typedef struct {
+  GQueue *updates;
+  GskRenderNode *last;
+  GtkWidget *widget;
+  guint tick_callback;
+  gulong unmap_callback;
+} GtkWidgetUpdates;
+
+struct _GtkUpdatesOverlay
+{
+  GtkInspectorOverlay parent_instance;
+
+  GHashTable *toplevels; /* widget => GtkWidgetUpdates */
+};
+
+struct _GtkUpdatesOverlayClass
+{
+  GtkInspectorOverlayClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkUpdatesOverlay, gtk_updates_overlay, GTK_TYPE_INSPECTOR_OVERLAY)
+
+static void
+gtk_update_free (gpointer data)
+{
+  GtkUpdate *region = data;
+
+  cairo_region_destroy (region->region);
+  g_slice_free (GtkUpdate, region);
+}
+
+static void
+gtk_widget_updates_release_widget (GtkWidgetUpdates *updates)
+{
+  g_assert (updates->widget);
+  g_signal_handler_disconnect (updates->widget, updates->unmap_callback);
+  if (updates->tick_callback)
+    gtk_widget_remove_tick_callback (updates->widget, updates->tick_callback);
+  updates->tick_callback = 0;
+  updates->widget = NULL;
+}
+
+static void
+gtk_widget_updates_free (gpointer data)
+{
+  GtkWidgetUpdates *updates = data;
+
+  g_queue_free_full (updates->updates, gtk_update_free);
+  g_clear_pointer (&updates->last, gsk_render_node_unref);
+  if (updates->widget)
+    gtk_widget_updates_release_widget (updates);
+
+  g_slice_free (GtkWidgetUpdates, updates);
+}
+
+static gboolean
+gtk_widget_updates_tick (GtkWidget     *widget,
+                         GdkFrameClock *clock,
+                         gpointer       data)
+{
+  GtkWidgetUpdates *updates = data;
+  GtkUpdate *draw;
+  gint64 now;
+
+  now = gdk_frame_clock_get_frame_time (clock);
+
+  for (draw = g_queue_pop_tail (updates->updates);
+       draw != NULL && (now - draw->timestamp >= GDK_DRAW_REGION_MAX_DURATION);
+       draw = g_queue_pop_tail (updates->updates))
+    {
+      gtk_update_free (draw);
+    }
+
+  gdk_surface_queue_expose (gtk_widget_get_surface (widget));
+  if (draw)
+    {
+      g_queue_push_tail (updates->updates, draw);
+      return G_SOURCE_CONTINUE;
+    }
+  else
+    {
+      updates->tick_callback = 0;
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static GtkWidgetUpdates *
+gtk_update_overlay_lookup_for_widget (GtkUpdatesOverlay *self,
+                                      GtkWidget         *widget,
+                                      gboolean           create)
+{
+  GtkWidgetUpdates *updates = g_hash_table_lookup (self->toplevels, widget);
+
+  if (updates || !create)
+    return updates;
+
+  updates = g_slice_new0 (GtkWidgetUpdates);
+  updates->updates = g_queue_new ();
+  updates->widget = widget;
+  updates->unmap_callback = g_signal_connect_swapped (widget, "unmap", G_CALLBACK (gtk_widget_updates_release_widget), updates);
+
+  g_hash_table_insert (self->toplevels, g_object_ref (widget), updates);
+  return updates;
+}
+
+static void
+gtk_widget_updates_add (GtkWidgetUpdates *updates,
+                        gint64            timestamp,
+                        cairo_region_t   *region)
+{
+  GtkUpdate *update;
+  GList *l;
+
+  update = g_slice_new0 (GtkUpdate);
+  update->timestamp = timestamp;
+  update->region = region;
+  for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next)
+    {
+      GtkUpdate *u = l->data;
+      cairo_region_subtract (u->region, region);
+    }
+  g_queue_push_head (updates->updates, update);
+  if (updates->tick_callback == 0)
+    updates->tick_callback = gtk_widget_add_tick_callback (updates->widget, gtk_widget_updates_tick, updates, NULL);
+}
+
+static void
+gtk_updates_overlay_snapshot (GtkInspectorOverlay *overlay,
+                              GtkSnapshot         *snapshot,
+                              GskRenderNode       *node,
+                              GtkWidget           *widget)
+{
+  GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay);
+  GtkWidgetUpdates *updates;
+  GtkUpdate *draw;
+  gint64 now;
+  GList *l;
+
+  updates = gtk_update_overlay_lookup_for_widget (self, widget, TRUE);
+  now = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
+
+  if (updates->last)
+    {
+      cairo_region_t *diff;
+
+      diff = cairo_region_create ();
+      gsk_render_node_diff (updates->last, node, diff);
+      if (cairo_region_is_empty (diff))
+        cairo_region_destroy (diff);
+      else
+        gtk_widget_updates_add (updates, now, diff);
+    }
+  else
+    {
+      cairo_region_t *region;
+      graphene_rect_t bounds;
+
+      gsk_render_node_get_bounds (node, &bounds);
+      region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
+                                                  floor (bounds.origin.x),
+                                                  floor (bounds.origin.y),
+                                                  ceil (bounds.origin.x + bounds.size.width) - floor (bounds.origin.x),
+                                                  ceil (bounds.origin.y + bounds.size.height) - floor (bounds.origin.y)
+                                              });
+      gtk_widget_updates_add (updates, now, region);
+    }
+  g_clear_pointer (&updates->last, gsk_render_node_unref);
+  updates->last = gsk_render_node_ref (node);
+
+  for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next)
+    {
+      double progress;
+      guint i;
+
+      draw = l->data;
+
+      if (now - draw->timestamp < GDK_DRAW_REGION_MIN_DURATION)
+        progress = 0.0;
+      else if (now - draw->timestamp < GDK_DRAW_REGION_MAX_DURATION)
+        progress = (double) (now - draw->timestamp - GDK_DRAW_REGION_MIN_DURATION)
+                   / (GDK_DRAW_REGION_MAX_DURATION - GDK_DRAW_REGION_MIN_DURATION);
+      else
+        break;
+
+      for (i = 0; i < cairo_region_num_rectangles (draw->region); i++)
+        {
+          GdkRectangle rect;
+
+          cairo_region_get_rectangle (draw->region, i, &rect);
+          gtk_snapshot_append_color (snapshot,
+                                     &(GdkRGBA) { 1, 0, 0, 0.4 * (1 - progress) },
+                                     &GRAPHENE_RECT_INIT(rect.x, rect.y, rect.width, rect.height),
+                                     "Debug Updates<%g>", progress);
+        }
+    }
+}
+
+static void
+gtk_updates_overlay_queue_draw (GtkInspectorOverlay *overlay)
+{
+  GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay);
+  GHashTableIter iter;
+  gpointer widget;
+
+  g_hash_table_iter_init (&iter, self->toplevels);
+  while (g_hash_table_iter_next (&iter, &widget, NULL))
+    gdk_surface_queue_expose (gtk_widget_get_surface (widget));
+}
+
+static void
+gtk_updates_overlay_dispose (GObject *object)
+{
+  GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (object);
+
+  g_hash_table_unref (self->toplevels);
+
+  G_OBJECT_CLASS (gtk_updates_overlay_parent_class)->dispose (object);
+}
+
+static void
+gtk_updates_overlay_class_init (GtkUpdatesOverlayClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkInspectorOverlayClass *overlay_class = GTK_INSPECTOR_OVERLAY_CLASS (klass);
+
+  overlay_class->snapshot = gtk_updates_overlay_snapshot;
+  overlay_class->queue_draw = gtk_updates_overlay_queue_draw;
+
+  gobject_class->dispose = gtk_updates_overlay_dispose;
+}
+
+static void
+gtk_updates_overlay_init (GtkUpdatesOverlay *self)
+{
+  self->toplevels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gtk_widget_updates_free);
+}
+
+GtkInspectorOverlay *
+gtk_updates_overlay_new (void)
+{
+  return g_object_new (GTK_TYPE_UPDATES_OVERLAY, NULL);
+}
+
diff --git a/gtk/inspector/updatesoverlay.h b/gtk/inspector/updatesoverlay.h
new file mode 100644 (file)
index 0000000..88c89d2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_UPDATES_OVERLAY_H__
+#define __GTK_UPDATES_OVERLAY_H__
+
+#include "inspectoroverlay.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_UPDATES_OVERLAY             (gtk_updates_overlay_get_type ())
+G_DECLARE_FINAL_TYPE (GtkUpdatesOverlay, gtk_updates_overlay, GTK, UPDATES_OVERLAY, GtkInspectorOverlay)
+
+GtkInspectorOverlay *   gtk_updates_overlay_new                 (void);
+
+G_END_DECLS
+
+#endif /* __GTK_UPDATES_OVERLAY_H__ */
index 6619fc7d172c353d9a0c7d9ad75486a0e2b4bf93..4af9b9a8a2c128b69f58df2e2221af1a7eff04db 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "visual.h"
 
+#include "updatesoverlay.h"
+#include "window.h"
+
 #include "gtkadjustment.h"
 #include "gtkbox.h"
 #include "gtkcomboboxtext.h"
@@ -65,6 +68,7 @@ struct _GtkInspectorVisualPrivate
 
   GtkWidget *debug_box;
   GtkWidget *rendering_mode_combo;
+  GtkWidget *updates_switch;
   GtkWidget *baselines_switch;
   GtkWidget *layout_switch;
   GtkWidget *touchscreen_switch;
@@ -75,6 +79,8 @@ struct _GtkInspectorVisualPrivate
   GtkWidget *texture_rectangle_switch;
 
   GtkAdjustment *focus_adjustment;
+
+  GtkInspectorOverlay *updates_overlay;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorVisual, gtk_inspector_visual, GTK_TYPE_SCROLLED_WINDOW)
@@ -220,6 +226,41 @@ font_scale_entry_activated (GtkEntry           *entry,
     update_font_scale (vis, factor, TRUE, FALSE);
 }
 
+static void
+updates_activate (GtkSwitch          *sw,
+                  GParamSpec         *pspec,
+                  GtkInspectorVisual *vis)
+{
+  GtkInspectorVisualPrivate *priv = vis->priv;
+  GtkInspectorWindow *iw;
+  gboolean updates;
+
+  updates = gtk_switch_get_active (sw);
+  iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (vis)));
+  if (iw == NULL)
+    return;
+
+  if (updates)
+    {
+      if (priv->updates_overlay == NULL)
+        {
+          priv->updates_overlay = gtk_updates_overlay_new ();
+          gtk_inspector_window_add_overlay (iw, priv->updates_overlay);
+          g_object_unref (priv->updates_overlay);
+        }
+    }
+  else
+    {
+      if (priv->updates_overlay != NULL)
+        {
+          gtk_inspector_window_remove_overlay (iw, priv->updates_overlay);
+          priv->updates_overlay = NULL;
+        }
+    }
+
+  redraw_everything ();
+}
+
 static void
 baselines_activate (GtkSwitch *sw)
 {
@@ -860,6 +901,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass)
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/visual.ui");
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, rendering_mode_combo);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, updates_switch);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, direction_combo);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, baselines_switch);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, layout_switch);
@@ -885,6 +927,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment);
 
+  gtk_widget_class_bind_template_callback (widget_class, updates_activate);
   gtk_widget_class_bind_template_callback (widget_class, direction_changed);
   gtk_widget_class_bind_template_callback (widget_class, rendering_mode_changed);
   gtk_widget_class_bind_template_callback (widget_class, baselines_activate);
index aaf475cef9f2fb5b9a36d0df527e76b92327180c..14433754b61002917e8dfea95fbcff3a4564f571 100644 (file)
                     </child>
                   </object>
                 </child>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="activatable">0</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="margin">10</property>
+                        <property name="spacing">40</property>
+                        <child>
+                          <object class="GtkLabel" id="updates_label">
+                            <property name="label" translatable="yes">Show Graphic Updates</property>
+                            <property name="halign">start</property>
+                            <property name="valign">baseline</property>
+                            <property name="xalign">0.0</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkSwitch" id="updates_switch">
+                            <property name="halign">end</property>
+                            <property name="valign">baseline</property>
+                            <property name="hexpand">1</property>
+                            <signal name="notify::active" handler="updates_activate"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
                 <child>
                   <object class="GtkListBoxRow">
                     <property name="activatable">0</property>
       <widget name="hidpi_label"/>
       <widget name="animation_label"/>
       <widget name="rendering_mode_label"/>
+      <widget name="updates_label"/>
       <widget name="baselines_label"/>
       <widget name="layout_label"/>
       <widget name="touchscreen_label"/>
index 13b0ff7b6cd80a84c2b5d88bb8081b3c3768ef75..adc55fcf1f7a60940aac148ea905dad73984587b 100644 (file)
@@ -427,7 +427,7 @@ gtk_inspector_prepare_render (GtkWidget            *widget,
 
       for (l = iw->overlays; l; l = l->next)
         {
-          gtk_inspector_overlay_snapshot (l->data, snapshot, widget);
+          gtk_inspector_overlay_snapshot (l->data, snapshot, node, widget);
         }
 
       gsk_render_node_unref (node);